package per.jdz.circuitbreaker;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

public class MethodWrapper implements Delayed {

    private Object o;

    private Method method;

    private Object[] parameters;

    private long expire;

    private int retryCount;

    private RequestRateCalculator requestRate;

    public RequestRateCalculator getRequestRate() {
        return requestRate;
    }

    public void setRequestRate(RequestRateCalculator requestRate) {
        this.requestRate = requestRate;
    }

    public Object getO() {
        return o;
    }

    public void setO(Object o) {
        this.o = o;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    public Object[] getParameters() {
        return parameters;
    }

    public void setParameters(Object[] parameters) {
        this.parameters = parameters;
    }

    public long getExpire() {
        return expire;
    }

    public void setExpire(long expire) {
        this.expire = expire;
    }

    public int getRetryCount() {
        return retryCount;
    }

    public void setRetryCount(int retryCount) {
        this.retryCount = retryCount;
    }

    public void incrRetry() {
        this.retryCount++;
    }

    public MethodWrapper(Method method, Object o, RequestRateCalculator requestRate) {
        this.o = o;
        this.method = method;
        this.requestRate = requestRate;
    }

    public Object invoke() throws InvocationTargetException, IllegalAccessException {
        return method.invoke(o, parameters);
    }

    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(this.expire - System.currentTimeMillis(), unit);
    }

    @Override
    public int compareTo(Delayed o) {
        return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
    }

    @Override
    public String toString() {
        return super.toString();
    }

    @Override
    public boolean equals(Object o1) {
        if (this == o1)
            return true;
        if (o1 == null || getClass() != o1.getClass())
            return false;
        MethodWrapper that = (MethodWrapper) o1;
        return expire == that.expire && retryCount == that.retryCount && Objects.equals(o, that.o)
                && Objects.equals(method, that.method) && Arrays.equals(parameters, that.parameters)
                && Objects.equals(requestRate, that.requestRate);
    }

    @Override
    public int hashCode() {
        int result = Objects.hash(o, method, expire, retryCount, requestRate);
        result = 31 * result + Arrays.hashCode(parameters);
        return result;
    }

}